/*
 * Code for blog.techhead.biz
 * Distributed under BSD-style license
 */

package biz.techhead.funcy;

import java.util.NoSuchElementException;

/**
 * A Step knows how to increment or decrement a value by a certain amount.
 *
 * <p>A Step should be Thread safe, but its returned {@link Step.Stepper}
 * should only be used by a single Thread.
 *
 * <p>The obvious example here is an Integer Step.
 *
 * <code><pre>&nbsp;{@literal
 * class IntegerStep implements Step<Integer> {
 *
 *     private final int amount;
 *
 *     IntegerStep(int amount) {
 *         this.amount = amount;
 *     }
 *
 *     public Stepper<Integer> stepper(final Integer point) {
 *         return new Stepper<Integer>() {
 *             int value = point;
 *             public Integer value() { return value; }
 *             public Integer stepUp() { return value += amount; }
 *             public Integer stepDown() { return value -= amount; }
 *         };
 *     }
 * }
 *
 * Step<Integer> byOne = new IntegerStep(1);
 * }</pre></code>
 *
 * But the concept applies to non-Number objects as well.
 *
 * <code><pre>&nbsp;{@literal
 * import java.util.Date;
 * import java.util.Calendar;
 *
 * class DateStep implements Step<Date> {
 *
 *     private final int calendarField;
 *
 *     DateStep(int calendarField) {
 *         this.calendarField = calendarField;
 *     }
 *
 *     public Stepper<Date> stepper(Date point) {
 *         final Calendar cal = Calendar.getInstance();
 *         cal.setTime(point);
 *         return new Stepper<Date>() {
 *
 *             public Date value() { return cal.getTime(); }
 *             public Date stepUp() { return step(1); }
 *             public Date stepDown() { return step(-1); }
 *
 *             Date step(int i) {
 *                 cal.add(calendarField, i);
 *                 return cal.getTime();
 *             }
 *         };
 *     }
 * }
 *
 * Step<Date> byDate = new DateStep(Calendar.DATE);
 * }</pre></code>
 *
 * @param <T> The type of element this Step works on
 *
 * @author Jonathan Hawkes <jhawkes at techhead.biz>
 */
public interface Step<T> {

    /**
     * Returns a stateful Stepper for the given object.
     */
    Stepper<T> stepper(T point);

    /**
     * A Stepper is a stateful object that is aware of the next (or previous)
     * value in line for a certain type of object.  It is analogous to a slide
     * on a ruler that can be moved up or down to point to a particular number.
     *
     * @param <T> The type of object to manipulate
     */
    interface Stepper<T> {

        /**
         * Returns the current value the Stepper holds.
         */
        T value();

        /**
         * Returns the next value in line and adjusts the position of this
         * Stepper to point to it.
         * @throws NoSuchElementException if there is no next value
         */
        T stepUp();

        /**
         * Returns the previous value in line and adjusts the positions of this
         * Stepper to point to it.
         * @throws NoSuchElementException if there is no previous value
         */
        T stepDown();
    }
}
